package com.shanyi.common.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * @author shanyi
 * @date 2024/11/23
 */
@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    /**
     * 配置 redis 连接工厂
     * 意义：LettuceConnectionFactory 是连接 Redis 服务器的入口，它使用了 Lettuce 客户端，并且配置了连接池来提高性能和资源管理
     * @return {@link LettuceConnectionFactory }
     */
    @Bean
    public LettuceConnectionFactory redisConnectionFactory() {
        // 配置redis服务器的连接信息
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setHostName("120.55.240.32");
        redisStandaloneConfiguration.setPort(6379);
        redisStandaloneConfiguration.setPassword("123456");
        redisStandaloneConfiguration.setDatabase(0);

        // 配置连接池
        GenericObjectPoolConfig<Object> poolConfig = new GenericObjectPoolConfig<>();
        poolConfig.setMaxTotal(100);
        poolConfig.setMaxIdle(50);
        poolConfig.setMinIdle(1);
        poolConfig.setMaxWaitMillis(2000);

        // 创建一个带有连接池配置的Lettuce客户端配置
        LettucePoolingClientConfiguration lettucePoolingClientConfiguration =
                LettucePoolingClientConfiguration.builder().poolConfig(poolConfig).build();

        // 返回带有连接池配置的redis连接工厂
        return new LettuceConnectionFactory(redisStandaloneConfiguration, lettucePoolingClientConfiguration);
    }

    /**
     * 配置并返回一个 RedisTemplate 实例，用于执行 Redis 操作
     * 意义: RedisTemplate 提供了一种高级抽象，使得开发者可以通过模板方法操作 Redis，而无需直接处理底层的 Redis 命令。
     * 它支持多种 Redis 操作，例如值操作、哈希操作、列表操作等
     * @return {@link RedisTemplate }<{@link String }, {@link Object }>
     */
    @Bean(name = "redisTemplate")
    public RedisTemplate<String, Object> redisTemplate() {

        /*
            1.创建 RedisTemplate: 这是 Spring 用于与 Redis 交互的核心类，简化了与 Redis 的交互。
            2.设置连接工厂: 使用前面定义的 LettuceConnectionFactory。
            3.设置序列化器: 设置键和值的序列化器，这里使用 StringRedisSerializer 来将键和值序列化为字符串。
        */

        // 创建一个新的RedisTemplate实例，用于操作Redis
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        // 设置RedisTemplate使用的连接工厂，以便它能够连接到Redis服务器
        redisTemplate.setConnectionFactory(redisConnectionFactory());

        // 创建一个StringRedisSerializer实例，用于序列化Redis的key为字符串
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // 创建一个ObjectMapper实例，用于处理JSON的序列化和反序列化
        ObjectMapper objectMapper = new ObjectMapper();
        // 设置ObjectMapper的属性访问级别，以便能够序列化对象的所有属性
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 启用默认的类型信息，以便在反序列化时能够知道对象的实际类型
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);

        // 创建一个Jackson2JsonRedisSerializer实例，用于序列化Redis的value为JSON格式
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        // 设置Jackson2JsonRedisSerializer使用的ObjectMapper
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        // 设置RedisTemplate的key序列化器为stringRedisSerializer
        redisTemplate.setKeySerializer(stringRedisSerializer);
        // 设置RedisTemplate的value序列化器为jackson2JsonRedisSerializer
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        // 设置RedisTemplate的hash key序列化器为stringRedisSerializer
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        // 设置RedisTemplate的hash value序列化器为jackson2JsonRedisSerializer
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        // 调用RedisTemplate的afterPropertiesSet方法，该方法会执行一些初始化操作，比如检查序列化器是否设置等
        redisTemplate.afterPropertiesSet();

        return redisTemplate;
    }
}
